/* ================================================================
*   Copyright (C) 2020 All rights reserved.
*
*   文件名称：example.cpp
*   创 建 者：xunmenglong
*   创建日期：2020年12月03日
*   描    述：
*
================================================================ */
#ifndef EXAMPLE_CPP
#define EXAMPLE_CPP

#include "xproto.h"
#include "xutil.h"

using namespace xutil;

int parse_binary_data_by_idx(char * binary_data) {
    xprotogetter g;
    int * handler = g.init(binary_data);

    // 获取字符串, title, url
    char * title = g.v1(handler, 0);
    LOG(NOTICE) << "title: " << title;
    char * url = g.v1(handler, 1);
    LOG(NOTICE) << "url: " << url;
    // 读取int, time
    int time = *((int *)g.v1(handler, 2));
    LOG(NOTICE) << "time: " << time;
    // 读取简单数组, areas, friend_ids
    LOG(NOTICE) << "第一种读取数组的方法: ";
    int areas_len = g.l1(handler, 3);
    for (int i=0; i<areas_len; i++) {
        char * cur_area = g.v2(handler, 3, i);
        LOG(NOTICE) << "areas[" << i << "]: " << cur_area;
    }
    LOG(NOTICE) << "第二种读取数组的方法: ";
    int * areas_handler = g.o1(handler, 3);
    areas_len = g.l0(areas_handler);
    for (int i=0; i<areas_len; i++) {
        char * cur_area = g.v1(areas_handler, i);
        LOG(NOTICE) << "area[" << i << "]: " << cur_area;
    }
    int friend_ids_len = g.l1(handler, 4);
    for (int i=0; i<friend_ids_len; i++) {
        int cur_friend_id = *(int *)g.v2(handler, 4, i);
        LOG(NOTICE) << "friend_ids[" << i << "]: " << cur_friend_id;
    }
    // 读取嵌套对象
    // 直接读取的方式
    char * author_name = g.v2(handler, 5, 0);
    LOG(NOTICE) << "author.name: " << author_name;
    LOG(NOTICE) << "author.followers_count: " << *(int *)g.v2(handler, 5, 1);
    // 间接读取的方式
    int * author_handler = g.o1(handler, 5);
    author_name = g.v1(author_handler, 0);
    LOG(NOTICE) << "author.name: " << author_name;
    // 读取数组
    int tags_len = g.l1(author_handler, 2);
    for (int i=0; i<tags_len; i++) {
        LOG(NOTICE) << "author.tags[" << i << "]: " << g.v2(author_handler, 2, i);
    }
    // 读取嵌套城市信息
    LOG(NOTICE) << "author.location.simple.province: " << g.v3(author_handler, 3, 0, 0);
    LOG(NOTICE) << "author.location.simple.city: " << g.v3(author_handler, 3, 0, 1);
    LOG(NOTICE) << "author.location.detail: " << g.v2(author_handler, 3, 1);

    // 读取最复杂的数组对象
    int followers_len = g.l1(handler, 6);
    for (int i=0; i<followers_len; i++) {
        int * cur_obj_handler = g.o2(handler, 6, i);
        LOG(NOTICE) << "followers[" << i << "].nickname: " << g.v1(cur_obj_handler, 0);
        LOG(NOTICE) << "followers[" << i << "].nickid: " << *(int *)g.v1(cur_obj_handler, 1);
        int hot_articles_len = g.l1(cur_obj_handler, 2);
        for (int j=0; j<hot_articles_len; j++) {
            LOG(NOTICE) << "followers[" << i << "].hot_articles[" << j
                << "].title: " << g.v3(cur_obj_handler, 2, j, 0);
            LOG(NOTICE) << "followers[" << i << "].hot_articles[" << j
                << "].url: " << g.v3(cur_obj_handler, 2, j, 1);
        }
        for (int j=0; j<g.l1(cur_obj_handler, 3); j++) {
            LOG(NOTICE) << "followers[" << i << "].tags[" << j
                << "]: " << g.v2(cur_obj_handler, 3, j);
        }
    }
    return 0;
}

int parse_binary_data_by_str(char * binary_data, schema_t * schema) {
    xprotogetter g;
    int * handler = g.init(binary_data);
    LOG(NOTICE) << "title: " << g.v_str(handler, schema, "title");
    LOG(NOTICE) << "url: " << g.v_str(handler, schema, "url");
    LOG(NOTICE) << "time: " << *(int *)g.v_str(handler, schema, "time");
    LOG(NOTICE) << "areas[0]: " << g.v_str(handler, schema, "areas[0]");
    LOG(NOTICE) << "areas[1]: " << g.v_str(handler, schema, "areas[1]");
    LOG(NOTICE) << "areas[2]: " << g.v_str(handler, schema, "areas[2]");
    LOG(NOTICE) << "friend_ids[0]: " << *(int *)g.v_str(handler, schema, "friend_ids[0]");
    LOG(NOTICE) << "friend_ids[1]: " << *(int *)g.v_str(handler, schema, "friend_ids[1]");
    LOG(NOTICE) << "friend_ids[2]: " << *(int *)g.v_str(handler, schema, "friend_ids[2]");
    LOG(NOTICE) << "friend_ids[3]: " << *(int *)g.v_str(handler, schema, "friend_ids[3]");
    LOG(NOTICE) << "author.name: " << g.v_str(handler, schema, "author.name");
    LOG(NOTICE) << "author.followers_count: " << *(int *)g.v_str(handler, schema, "author.followers_count");
    LOG(NOTICE) << "author.name: " << g.v_str(handler, schema, "author.name");
    LOG(NOTICE) << "author.tags[0]: " << g.v_str(handler, schema, "author.tags[0]");
    LOG(NOTICE) << "author.tags[1]: " << g.v_str(handler, schema, "author.tags[1]");
    LOG(NOTICE) << "author.tags[2]: " << g.v_str(handler, schema, "author.tags[2]");
    LOG(NOTICE) << "author.location.simple.province: " << g.v_str(handler, schema, "author.location.simple.province");
    LOG(NOTICE) << "author.location.simple.city: " << g.v_str(handler, schema, "author.location.simple.city");
    LOG(NOTICE) << "author.location.detail: " << g.v_str(handler, schema, "author.location.detail");
    LOG(NOTICE) << "followers[0].nickname: " << g.v_str(handler, schema, "followers[0].nickname");
    LOG(NOTICE) << "followers[0].nickid: " << *(int *)g.v_str(handler, schema, "followers[0].nickid");
    LOG(NOTICE) << "followers[0].hot_articles[0].title: " << g.v_str(handler, schema, "followers[0].hot_articles[0].title");
    LOG(NOTICE) << "followers[0].hot_articles[0].url: " << g.v_str(handler, schema, "followers[0].hot_articles[0].url");
    LOG(NOTICE) << "followers[0].hot_articles[1].title: " << g.v_str(handler, schema, "followers[0].hot_articles[1].title");
    LOG(NOTICE) << "followers[0].hot_articles[1].url: " << g.v_str(handler, schema, "followers[0].hot_articles[1].url");
    LOG(NOTICE) << "followers[0].tags[0]: " << g.v_str(handler, schema, "followers[0].tags[0]");
    LOG(NOTICE) << "followers[0].tags[1]: " << g.v_str(handler, schema, "followers[0].tags[1]");
    LOG(NOTICE) << "followers[1].nickname: " << g.v_str(handler, schema, "followers[1].nickname");
    LOG(NOTICE) << "followers[1].nickid: " << *(int *)g.v_str(handler, schema, "followers[1].nickid");
    LOG(NOTICE) << "followers[1].hot_articles[0].title: " << g.v_str(handler, schema, "followers[1].hot_articles[0].title");
    LOG(NOTICE) << "followers[1].hot_articles[0].url: " << g.v_str(handler, schema, "followers[1].hot_articles[0].url");
    LOG(NOTICE) << "followers[1].hot_articles[1].title: " << g.v_str(handler, schema, "followers[1].hot_articles[1].title");
    LOG(NOTICE) << "followers[1].hot_articles[1].url: " << g.v_str(handler, schema, "followers[1].hot_articles[1].url");
    LOG(NOTICE) << "followers[1].tags[0]: " << g.v_str(handler, schema, "followers[1].tags[0]");
    LOG(NOTICE) << "followers[1].tags[1]: " << g.v_str(handler, schema, "followers[1].tags[1]");
    LOG(NOTICE) << "followers[1].tags[2]: " << g.v_str(handler, schema, "followers[1].tags[2]");
    return 0;
}

int str_to_idx_arr(char * binary_data, schema_t * schema) {
    xprotogetter g;

    const char * expr_str = "followers[1].hot_articles[1].title";
    int idx_arr[16];
    int arr_len;
    field_t * field;
    LOG(NOTICE) << "begin to convert expr_str: " << expr_str;
    int ret = g.get_idxs_by_str(schema, expr_str, idx_arr, &arr_len, &field);
    if (ret != 0){
        LOG(ERROR) << "get_idxs_by_str fail: " << expr_str;
        return -1;
    }
    string idx_arr_str = "[";
    for (int i=0; i<arr_len; i++) {
        idx_arr_str += to_string(idx_arr[i]) + ",";
    }
    if (idx_arr_str.size() > 1) {
        idx_arr_str.erase(idx_arr_str.size()-1);
    }
    idx_arr_str += "]";

    int * handler = g.init(binary_data);
    char * cur_expr_value = g.v_n(handler, idx_arr, arr_len);
    LOG(NOTICE) << "convert_succ, idx_arr: " << idx_arr_str 
        << ", value: " << cur_expr_value;
    return 0;
}

int arr_str_demo(char * binary_data, schema_t * schema) {
    xprotogetter g;
    //const char * expr_str = "followers[].hot_articles[].title";
    //const char * expr_str = "author.tags";
    const char * expr_str = "followers[].tags[]";
    field_t * fields[16];
    int fields_len;
    int ret = g.get_fields_by_str(schema, expr_str, fields, &fields_len);
    if (ret != 0) {
        LOG(ERROR) << "get_fields_by_str fail: " << expr_str;
        return -1;
    }

    int * handler = g.init(binary_data);
    string result;
    ret = g.get_strs_by_fields(handler, fields, fields_len, result);
    if (ret != 0) {
        LOG(ERROR) << "get_strs_by_fields fail";
        return -1;
    }
    LOG(NOTICE) << "result: " << result;
    return 0;
}

int main(int argc, char ** argv) {
    // 初始化proto对象, 序列化是线程安全的, proto可以是全局变量
    xproto xp;
    const char * schema_file = "./doc.schema.json";
    string schema_str = file_get_contents(schema_file);
    int ret = xp.init(schema_str.c_str());
    if (ret != 0) {
        LOG(ERROR) << "init xproto fail: " << schema_file;
        return -1;
    }
    LOG(NOTICE) << "init schema succ";

    // 序列化存储空间, 其中head和body是临时空间
    // 最终结果存放在out_data当中
    char * head_ptr = (char *)malloc(10240);
    char * body_ptr = (char *)malloc(10240);
    char * out_data = (char *)malloc(10240*2);

    // 执行序列化, 先读取示例json字符串
    const char * json_data_file = "./doc.json";
    string json_data_str = file_get_contents(json_data_file);
    int len = xp.serialize(json_data_str.c_str(), out_data, head_ptr, body_ptr);
    if (len < 0) {
        LOG(ERROR) << "serialize json to binary fail";
        return -1;
    }
    LOG(NOTICE) << "serialize succ, binary_len: " << len
        << ", json_len: " << json_data_str.size();

    schema_t * schema = xp.get_schema();

    /**
     * 开始解析二进制数据, 如下有两种示例：
     * 1. 按照索引来获取值
     * 2. 按照表达式来获取值
     *
     * 按表达式会比按索引多一步将表达式转化为索引的过程，效率会更低
     * 我们默认一般用户会对自己json的schema是熟悉的，所以一般推荐用索引的方式获取
     *
     * 如果有多个同样格式的二进制包需要解析的话
     * 建议先用`get_idxs_by_str`将表达式转换为索引数组, 然后再用索引数组获得值, 提高效率
     */
    LOG(NOTICE) << "======== 按照索引的方式获取值 =======";
    ret = parse_binary_data_by_idx(out_data);
    if (ret != 0) {
        LOG(ERROR) << "parse_binary_data_by_idx fail";
        return -1;
    }
    LOG(NOTICE) << "======== 结束 =======\n";

    LOG(NOTICE) << "======== 按照表达式的方式获取值 =======";
    ret = parse_binary_data_by_str(out_data, schema);
    if (ret != 0) {
        LOG(ERROR) << "parse_binary_data_by_str fail";
        return -1;
    }
    LOG(NOTICE) << "======== 结束 =======\n";

    LOG(NOTICE) << "======== 表达式转换到索引数组 =======";
    ret = str_to_idx_arr(out_data, schema);
    if (ret != 0) {
        LOG(ERROR) << "str_to_idx_arr fail";
        return -1;
    }
    LOG(NOTICE) << "======== 结束 =======\n";

    LOG(NOTICE) << "======== 自动遍历数组的函数演示 =======";
    ret = arr_str_demo(out_data, schema);
    LOG(NOTICE) << "======== 结束 =======\n";
    return 0;
}

#endif
